package utils

import (
	"encoding/json"
	"fmt"
)

// AuthConfigParser parses authentication configuration from OpenAPI operations
type AuthConfigParser struct{}

// NewAuthConfigParser creates a new AuthConfigParser instance
func NewAuthConfigParser() *AuthConfigParser {
	return &AuthConfigParser{}
}

// ParseAuthConfig parses the x-mcp-auth extension field from an OpenAPI operation
func (acp *AuthConfigParser) ParseAuthConfig(operation map[string]interface{}) (*AuthConfig, error) {
	// Look for x-mcp-auth extension field
	authExtension, exists := operation["x-mcp-auth"]
	if !exists {
		return nil, nil // No auth config found, which is valid
	}

	// Convert to map for easier processing
	authMap, ok := authExtension.(map[string]interface{})
	if !ok {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth must be an object", 
			fmt.Sprintf("got type: %T", authExtension))
	}

	// Create AuthConfig and populate fields
	config := &AuthConfig{
		Metadata: make(map[string]string),
	}

	// Parse type field (required)
	if typeVal, ok := authMap["type"].(string); ok {
		config.Type = typeVal
	} else if _, exists := authMap["type"]; exists {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth.type must be a string", "")
	}

	// Parse token_path field
	if tokenPath, ok := authMap["token_path"].(string); ok {
		config.TokenPath = tokenPath
	} else if _, exists := authMap["token_path"]; exists {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth.token_path must be a string", "")
	}

	// Parse token_format field
	if tokenFormat, ok := authMap["token_format"].(string); ok {
		config.TokenFormat = tokenFormat
	} else if _, exists := authMap["token_format"]; exists {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth.token_format must be a string", "")
	}

	// Parse header_name field
	if headerName, ok := authMap["header_name"].(string); ok {
		config.HeaderName = headerName
	} else if _, exists := authMap["header_name"]; exists {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth.header_name must be a string", "")
	}

	// Parse query_param field
	if queryParam, ok := authMap["query_param"].(string); ok {
		config.QueryParam = queryParam
	} else if _, exists := authMap["query_param"]; exists {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth.query_param must be a string", "")
	}

	// Parse token_prefix field
	if tokenPrefix, ok := authMap["token_prefix"].(string); ok {
		config.TokenPrefix = tokenPrefix
	} else if _, exists := authMap["token_prefix"]; exists {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth.token_prefix must be a string", "")
	}

	// Parse expires_path field
	if expiresPath, ok := authMap["expires_path"].(string); ok {
		config.ExpiresPath = expiresPath
	} else if val, exists := authMap["expires_path"]; exists && val != nil {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"x-mcp-auth.expires_path must be a string", "")
	}

	// Parse requires field (array of strings)
	if requiresVal, exists := authMap["requires"]; exists {
		if requiresArray, ok := requiresVal.([]interface{}); ok {
			for i, req := range requiresArray {
				if reqStr, ok := req.(string); ok {
					config.Requires = append(config.Requires, reqStr)
				} else {
					return nil, NewAuthError(ErrTypeConfigInvalid, 
						fmt.Sprintf("x-mcp-auth.requires[%d] must be a string", i), "")
				}
			}
		} else {
			return nil, NewAuthError(ErrTypeConfigInvalid, 
				"x-mcp-auth.requires must be an array", "")
		}
	}

	// Parse metadata field (object with string values)
	if metadataVal, exists := authMap["metadata"]; exists && metadataVal != nil {
		if metadataMap, ok := metadataVal.(map[string]interface{}); ok {
			for key, value := range metadataMap {
				if valueStr, ok := value.(string); ok {
					config.Metadata[key] = valueStr
				} else {
					return nil, NewAuthError(ErrTypeConfigInvalid, 
						fmt.Sprintf("x-mcp-auth.metadata.%s must be a string", key), "")
				}
			}
		} else {
			return nil, NewAuthError(ErrTypeConfigInvalid, 
				"x-mcp-auth.metadata must be an object", "")
		}
	}

	// Validate the parsed configuration
	if err := config.Validate(); err != nil {
		return nil, err
	}

	return config, nil
}

// IsLoginEndpoint checks if an operation is a login endpoint
func (acp *AuthConfigParser) IsLoginEndpoint(operation map[string]interface{}) bool {
	config, err := acp.ParseAuthConfig(operation)
	if err != nil || config == nil {
		return false
	}
	return config.Type == "login"
}

// RequiresAuth returns the list of authentication types required by an operation
func (acp *AuthConfigParser) RequiresAuth(operation map[string]interface{}) []string {
	config, err := acp.ParseAuthConfig(operation)
	if err != nil || config == nil {
		return nil
	}
	return config.Requires
}

// ParseAuthConfigFromAPIEndpoint is a convenience method that works with APIEndpoint struct
func (acp *AuthConfigParser) ParseAuthConfigFromAPIEndpoint(endpoint APIEndpoint) (*AuthConfig, error) {
	// Convert APIEndpoint to a map for processing
	// This is a helper method to work with the existing parser structure
	endpointBytes, err := json.Marshal(endpoint)
	if err != nil {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"failed to marshal endpoint", err.Error())
	}

	var endpointMap map[string]interface{}
	if err := json.Unmarshal(endpointBytes, &endpointMap); err != nil {
		return nil, NewAuthError(ErrTypeConfigInvalid, 
			"failed to unmarshal endpoint", err.Error())
	}

	return acp.ParseAuthConfig(endpointMap)
}

// IsLoginEndpointFromAPIEndpoint checks if an APIEndpoint is a login endpoint
func (acp *AuthConfigParser) IsLoginEndpointFromAPIEndpoint(endpoint APIEndpoint) bool {
	config, err := acp.ParseAuthConfigFromAPIEndpoint(endpoint)
	if err != nil || config == nil {
		return false
	}
	return config.Type == "login"
}

// RequiresAuthFromAPIEndpoint returns the authentication requirements for an APIEndpoint
func (acp *AuthConfigParser) RequiresAuthFromAPIEndpoint(endpoint APIEndpoint) []string {
	config, err := acp.ParseAuthConfigFromAPIEndpoint(endpoint)
	if err != nil || config == nil {
		return nil
	}
	return config.Requires
}